SQLiteは軽量で高速ですが、業務アプリで使うと ロック(database is locked)・DB破損・遅延 といった問題が発生しがちです。 この記事では、これらのトラブルの原因・再現パターン・対策を 実務目線で徹底解説します。
・database is locked の根本原因
・WALモードの正しい使い方
・DB破損の典型パターン
・遅延の原因と高速化テクニック
・ファイル共有の危険性
・業務アプリ向けベストプラクティス
1. SQLiteのロックとは?
SQLiteは単一ファイルDBであり、 同時書き込みは1つだけという制約があります。
■ 読み込み(SELECT)
- 複数同時OK
■ 書き込み(INSERT/UPDATE/DELETE)
- 同時1つだけ(直列)
この制約により、同時に書き込みが発生すると database is locked が発生します。
2. database is locked の原因トップ5
① 長時間トランザクション
UPDATEやINSERTを長時間保持すると、他の書き込みが待たされます。
② 同時書き込み(複数スレッド)
UIスレッドとバックグラウンドスレッドが同時に書き込むとロック。
③ ファイル共有(NAS/共有フォルダ)
最悪のパターン。破損の原因にもなる。
④ VACUUM中のアクセス
VACUUMは排他的ロックを取るため、他の操作が止まる。
⑤ WALモードの誤用
WALは万能ではなく、複数PCからのアクセスには弱い。
3. ロック対策(最重要)
■ 対策1:トランザクションを短くする
// 悪い例(長時間ロック)
using var tran = con.BeginTransaction();
Thread.Sleep(3000);
UPDATE...
tran.Commit();
書き込みは一瞬で終わらせるのが鉄則。
■ 対策2:書き込みは直列化(キュー方式)
複数スレッドから書き込む場合は、 BlockingCollection などでキュー化して直列処理にする。
■ 対策3:WALモードを使う
PRAGMA journal_mode = WAL;
読み込みと書き込みの競合が減る。
■ 対策4:接続は短く・使い捨て
接続を長時間保持するとロックが残りやすい。
■ 対策5:ファイル共有をやめる
SQLiteを共有フォルダで使うのは絶対にNG。 破損・ロック・遅延の温床。
4. DB破損の原因と対策
SQLiteは堅牢ですが、次のケースで破損しやすい。
■ 原因1:ネットワーク越しのDBアクセス
NAS・共有フォルダは破損の最大要因。
■ 原因2:アプリ強制終了
書き込み中にアプリが落ちると破損することがある。
■ 原因3:ディスクフル
書き込み途中で容量不足になると破損。
■ 原因4:WALファイルの消失
WALモードで -wal ファイルが消えると整合性が崩れる。
■ 対策
- ファイル共有をやめる
- WALモードを正しく使う
- バックアップを定期的に取る
- アプリ終了時にトランザクションを残さない
- ディスク容量を監視する
5. 遅延(パフォーマンス低下)の原因
① インデックス不足
WHERE句に使う列には必ずインデックスを貼る。
② 不必要なJOIN
SQLiteはJOINが弱い → 非正規化が有効。
③ 大量データの全件取得
LIMIT/OFFSETでページング必須。
④ VACUUM未実行(DB肥大化)
削除が多いDBは肥大化して遅くなる。
⑤ 同期処理(UIスレッドでDBアクセス)
async/awaitで非同期化する。
6. 遅延対策(高速化)
- インデックス最適化
- 非正規化テーブルの導入
- LIMIT/OFFSETでページング
- キャッシュ戦略(メモリ・結果・API)
- WALモード
- VACUUM / ANALYZE の定期実行
7. 業務アプリ向けベストプラクティス
- 書き込みは直列化(キュー方式)
- トランザクションは短く
- WALモードを使う(同一PCのみ)
- ファイル共有は絶対にしない
- インデックスを正しく貼る
- ページングで大量データを扱う
- バックアップを定期的に取る
まとめ:SQLiteのトラブルは“構成と設計”でほぼ防げる
- ロック → トランザクションと並列書き込みの問題
- 破損 → ファイル共有・強制終了・WAL誤用
- 遅延 → インデックス・JOIN・大量データ
SQLiteは正しく使えば非常に安定したDBです。 この記事の対策を取り入れることで、 ロック・破損・遅延のほぼすべてを防ぐことができます。